#include <vector>
#include <algorithm>
#include "Ghost.h"
#include "PacManGame.h"
using namespace std;



Ghost::Ghost( int id )
: nGhostId( id ), state(Ghost::SEEKING_PACMAN)
{
}

void Ghost::render( int nTexture )
{
	switch( getState( ) )
	{
		case Ghost::FLEEING_PACMAN:
			_render( Game::nGhostFleeTextures[ rand() % 2 ] );
			break;
		case Ghost::DEAD:
			_render( Game::nGhostEyesTexture );
			break;
		case Ghost::SEEKING_PACMAN:
		default:
			_render( nTexture );
			break;
	}
}

void Ghost::_render( int nTexture )
{
	glPushAttrib( GL_TEXTURE_BIT );
	glPushMatrix( );
		glTranslatef( X * Game::BLOCK_WIDTH, Y * Game::BLOCK_HEIGHT, 0 );
		glBindTexture( GL_TEXTURE_2D, nTexture );
		glCallList( Object::m_DisplayList );
	glPopMatrix( );
	glPopAttrib( );
}



Object *Ghost::_findPath( )
{ 
	return _findPath( Game::gameBoard[ this->Y * Game::GAME_BOARD_SIZE + this->X ],
					  Game::gameBoard[ Game::pPlayer->Y * Game::GAME_BOARD_SIZE + Game::pPlayer->X ] );
}

Object *Ghost::_findPath( const Object *pStart, const Object *pEnd )
{		
	BestFirstSearch<Object *, ManhattanDistanceHueristic, ObjectSuccessors>::Node *pPath;

	if( bestFirstSearch.find( const_cast<Object *>(pEnd), const_cast<Object *>(pStart) ) )
	{
		pPath = bestFirstSearch.getPath( );
		
		if( pPath->parent != NULL )
			return pPath->parent->state;
		else
			goto _findPath_error;
	}
	else
	{
		goto _findPath_error;
	}

_findPath_error:
	bestFirstSearch.cleanup( );
	return NULL;
}

Object *Ghost::_findPath( int offsetX, int offsetY )
{
	assert( offsetX != 0 || offsetY != 0 ); //use the other findPath()

	int pacmanX = Game::pPlayer->X + offsetX;
	int pacmanY = Game::pPlayer->Y + offsetY;

	if( pacmanY < 0 ) pacmanY = Game::GAME_BOARD_SIZE + pacmanY;
	if( pacmanX < 0 ) pacmanX = Game::GAME_BOARD_SIZE + pacmanX;
	if( pacmanY >= Game::GAME_BOARD_SIZE ) pacmanY = pacmanY - Game::GAME_BOARD_SIZE;
	if( pacmanX >= Game::GAME_BOARD_SIZE ) pacmanX = pacmanX - Game::GAME_BOARD_SIZE;

	Object *pPacmanBoardObj = Game::gameBoard[ pacmanY * Game::GAME_BOARD_SIZE + pacmanX ];
	Object *pGhostBoardObj = Game::gameBoard[ Y * Game::GAME_BOARD_SIZE + X ];
	
	if( pPacmanBoardObj->type == Object::OBJ_BLOCK )
	{
		ObjectSuccessors getSuccessors;
		vector<Object *> &successors = getSuccessors( pPacmanBoardObj );

		for( unsigned int i = 0; i < successors.size( ); i++ )
		{
			if( successors[ i ]->type != Object::OBJ_BLOCK )
			{
				pPacmanBoardObj = successors[ i ];
				break;
			}
		}
		
	}
	
	return _findPath( pGhostBoardObj, pPacmanBoardObj );
}


void Ghost::_pathCleanup( )
{ 
	bestFirstSearch.cleanup( );
}

void Ghost::flee( )
{
	Object *pPath = _findPath( Game::GAME_BOARD_SIZE >> 1, Game::GAME_BOARD_SIZE >> 1 );

	if( pPath != NULL )
	{
		if( !isCollision( pPath, this ) )
		{
			X = pPath->X;
			Y = pPath->Y;
		}
		_pathCleanup( );
	}
}


void Ghost::seek( )
{		
	Object *pPath = _findPath( );

	if( pPath != NULL )
	{
		if( !isCollision( pPath, this ) )
		{
			X = pPath->X;
			Y = pPath->Y;
		}
		_pathCleanup( );
	}
}

void Ghost::returnToHome( )
{	
	const int centerXY = Game::GAME_BOARD_SIZE >> 1;
	
	if( this->Y == centerXY && this->X == centerXY )
	{
		setState( Ghost::SEEKING_PACMAN );
	}	
	
	Object *pPath = _findPath( Game::gameBoard[ this->Y * Game::GAME_BOARD_SIZE + this->X ],
							   Game::gameBoard[ centerXY * Game::GAME_BOARD_SIZE + centerXY ] );

	if( pPath != NULL )
	{
		if( !isCollision( pPath, this ) )
		{
			X = pPath->X;
			Y = pPath->Y;
		}
		_pathCleanup( );
	}
}

bool Ghost::isCollision( Object *pObj, Ghost *pGhost )
{
	// wrap around...
	if( pObj->Y < 0 ) pObj->Y = Game::GAME_BOARD_SIZE + pObj->Y;
	if( pObj->X < 0 ) pObj->X = Game::GAME_BOARD_SIZE + pObj->X;
	if( pObj->Y >= Game::GAME_BOARD_SIZE ) pObj->Y = pObj->Y - Game::GAME_BOARD_SIZE;
	if( pObj->X >= Game::GAME_BOARD_SIZE ) pObj->X = pObj->X - Game::GAME_BOARD_SIZE;

	unsigned int index = pObj->Y * Game::GAME_BOARD_SIZE + pObj->X;
	
	ObjectType collidingObjectType = Game::gameBoard[ index ]->type;

	if( collidingObjectType == OBJ_BLOCK )
		return true;
	if( Game::pPlayer->X == pObj->X && Game::pPlayer->Y == pObj->Y )
	{	
			if( pGhost->getState( ) == Ghost::FLEEING_PACMAN )
			{
				Game::soundSystem->playSound( FMOD_CHANNEL_FREE, Game::soundEffects[ Game::SFX_GHOST_OR_PACMAN_EATEN ], false, 0 );
				pGhost->setState( Ghost::DEAD );
			}
			else if( pGhost->getState( ) == Ghost::SEEKING_PACMAN )
			{
				Game::soundSystem->playSound( FMOD_CHANNEL_FREE, Game::soundEffects[ Game::SFX_GHOST_OR_PACMAN_EATEN ], false, 0 );
				Game::nPlayerLives--;
				Game::state = Game::GAME_OVER;
			}
			return true;	
	}
	
	return false;
}

Ghost::GhostState Ghost::getState( ) const
{ return state; }

void Ghost::setState( GhostState s )
{ state = s; }

void Ghost::setAllGhostToFlee( )
{
	for( unsigned int i = 0; i < Game::nNumberOfGhosts; i++ )
	{
		Ghost *pGhost = static_cast<Ghost *>( Game::ghosts[ i ] );
		pGhost->setState( Ghost::FLEEING_PACMAN );
	}

}

void Ghost::setAllGhostToSeek( )
{
	for( unsigned int i = 0; i < Game::nNumberOfGhosts; i++ )
	{
		Ghost *pGhost = static_cast<Ghost *>( Game::ghosts[ i ] );
		pGhost->setState( Ghost::SEEKING_PACMAN );
	}

}